{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e44e945c",
   "metadata": {},
   "source": [
    "## 6.1 基础知识\n",
    "### 6.1.1 迭代器和可迭代对象\n",
    "1. **iter()** 与 **next()** 内置函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3b049971",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<list_iterator object at 0x0000013C37D93E50>\n",
      "<str_ascii_iterator object at 0x0000013C37D92140>\n"
     ]
    }
   ],
   "source": [
    "print(iter([1, 2, 3]))\n",
    "print(iter('foo'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84404ef7",
   "metadata": {},
   "source": [
    "- 迭代器：不断执行next()函数会返回下一代迭代结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d13ffdd2",
   "metadata": {},
   "outputs": [],
   "source": [
    "l = ['foo', 'bar']\n",
    "iter_l = iter(l)\n",
    "print(iter_l)\n",
    "print(next(iter_l))\n",
    "print(next(iter_l))\n",
    "print(next(iter_l))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ceb12ca",
   "metadata": {},
   "source": [
    "- 当对迭代器执行iter()函数时，尝试获取迭代器的迭代器对象时，返回的结果一定是迭代器本身"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "461aa9bf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "foo\n",
      "bar\n",
      "goobar\n",
      "foo\n",
      "bar\n",
      "goobar\n"
     ]
    }
   ],
   "source": [
    "names = ['foo', 'bar', 'goobar']\n",
    "for name in names:\n",
    "    print(name)\n",
    "\n",
    "iterator = iter(names)\n",
    "while(True):\n",
    "    try:\n",
    "        name = next(iterator)\n",
    "        print(name)\n",
    "    except StopIteration:\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54e52bc8",
   "metadata": {},
   "source": [
    "2. 自定义迭代器\n",
    "- 两个魔法方法： **__iter__**、**__next__**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "6172536b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n",
      "14\n",
      "17\n"
     ]
    }
   ],
   "source": [
    "class Range7:\n",
    "    \"\"\"生成某个范围可被7整除或包含7的数字\n",
    "    :param start:开始数字\n",
    "    :param end:结束数字\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, start, end):\n",
    "        self.start = start\n",
    "        self.end = end\n",
    "        #使用current保存当前所在位置\n",
    "        self.current = start\n",
    "\n",
    "    def __iter__(self):\n",
    "        return self\n",
    "    \n",
    "    def __next__(self):\n",
    "        while True:\n",
    "            #当已经到达边界时，抛出异常终止迭代\n",
    "            if self.current >= self.end:\n",
    "                raise StopIteration\n",
    "            \n",
    "            if self.num_is_valid(self.current):\n",
    "                ret = self.current\n",
    "                self.current += 1\n",
    "                return ret\n",
    "            self.current+=1\n",
    "\n",
    "    def num_is_valid(self, num):\n",
    "        \"\"\"判断数字是否满足要求\"\"\"\n",
    "        if num == 0:\n",
    "            return False\n",
    "        return num%7 == 0 or '7' in str(num)\n",
    "\n",
    "r = Range7(0, 20)\n",
    "for num in r:\n",
    "    print(num)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d13ad65",
   "metadata": {},
   "source": [
    "3. 区分迭代器与可迭代对象\n",
    "- 迭代器是可迭代对象的一种，每个迭代器对应一次完整的迭代过程，因此保存与迭代相关的状态-迭代位置\n",
    "- 判断一个对象是否可迭代，只需调用iter()，然后看结果是不是一个迭代器\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4c95f1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Range7:\n",
    "    \"\"\"生成某个范围可被7整除或包含7的数字\n",
    "    :param start:开始数字\n",
    "    :param end:结束数字\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, start, end):\n",
    "        self.start = start\n",
    "        self.end = end\n",
    "\n",
    "    def __iter__(self):\n",
    "        return Range7Iterator(self)\n",
    "class Range7Iterator:\n",
    "    def __init__(self, range_obj):\n",
    "        self.range_obj = range_obj\n",
    "        self.current = range_obj.start\n",
    "    def __iter__(self):\n",
    "        return self\n",
    "    def __next__(self):\n",
    "        while True:\n",
    "            if self.current >= self.range_obj.end:\n",
    "                raise StopIteration\n",
    "            if self.num_is_valid(self.current):\n",
    "                ret = self.current\n",
    "                self.current+=1\n",
    "                return ret\n",
    "            self.current+=1\n",
    "    def num_is_valid(self, num):\n",
    "        \"\"\"判断数字是否满足要求\"\"\"\n",
    "        if num == 0:\n",
    "            return False\n",
    "        return num%7 == 0 or '7' in str(num)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8f7ba76",
   "metadata": {},
   "source": [
    "- **__getitem__** 方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c83b0128",
   "metadata": {},
   "source": [
    "4. 生成器是迭代器\n",
    "- 生成器是一种“懒惰的”可迭代对象，使用它来代替传统列表可以节约内存，提升执行效率\n",
    "- 生成器是一种简化的执行器实现，使用它大大降低了实现传统迭代器的编码成本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e60d32ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "def range_7_gen(start, end):\n",
    "    \"\"\"生成器版本的Rang7Iterator\"\"\"\n",
    "    num = start\n",
    "    while num<end:\n",
    "        if num != 0 and (num % 7 == 0 or '7' in str(num)):\n",
    "            yield num \n",
    "        num+=1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3cd14f1c",
   "metadata": {},
   "source": [
    "### 6.1.2 修饰可迭代对象优化循环\n",
    "- 使用生成器函数修饰可迭代对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ebd38d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def even_only(numbers):\n",
    "    for num in numbers:\n",
    "        if num%2 == 0:\n",
    "            yield num\n",
    "\n",
    "def sum_even_only_v2(numbers):\n",
    "    \"\"\"对numbers里所有的偶数求和\"\"\"\n",
    "    result = 0\n",
    "    for num in even_only(numbers):\n",
    "        result += num\n",
    "    return result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0aac1bc",
   "metadata": {},
   "source": [
    "### 6.1.3 使用itertools模块优化循环\n",
    "1. 使用product()扁平化多层嵌套循环"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f462d143",
   "metadata": {},
   "outputs": [],
   "source": [
    "from itertools import product\n",
    "\n",
    "def find_twelve_v2(num_list1, num_list2, num_list3):\n",
    "    for num1, num2, num3 in product(num_list1, num_list2, num_list3):\n",
    "        if num1+num2+num3 == 12:\n",
    "            return num1, num2, num3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "835947df",
   "metadata": {},
   "source": [
    "2. 使用 **islice()** 实现循环内隔行处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4433b4ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "def parse_titles(filename):\n",
    "    \"\"\"从隔行数据文件中读取Reddit主题名称\n",
    "    \"\"\"\n",
    "    with open(filename, 'r') as fp:\n",
    "        for i, line in enumerate(fp):\n",
    "            #跳过无意义的 --- 分隔符\n",
    "            if i % 2 == 0:\n",
    "                yield line.strip()\n",
    "from itertools import islice\n",
    "\n",
    "def parse_titles_v2(filename):\n",
    "    with open(filename, 'r') as fp:\n",
    "        #设置step = 2,跳过无意义的 --- 分隔符\n",
    "        for line in islice(fp, 0, None, 2):\n",
    "            yield line.strip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2522d18",
   "metadata": {},
   "source": [
    "3. 使用 **takewhile()** 替代break语句"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "266efed6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from itertools import takewhile\n",
    "\n",
    "for user in takewhile(is_qualified, users):\n",
    "    \"\"\"进行处理\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4785dd69",
   "metadata": {},
   "source": [
    "### 6.1.4 循环语句的else关键字"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70e81219",
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_tasks(tasks):\n",
    "    \"\"\"批量处理任务，如果遇到状态不为pending的任务，则中止本次任务\"\"\"\n",
    "    for task in tasks:\n",
    "        if not task.is_pending():\n",
    "            notify_admin('Found non-pending task, process aborted')\n",
    "            break\n",
    "        process(task)\n",
    "    else:\n",
    "        notify_admin('All tasks was processed')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47ff0247",
   "metadata": {},
   "source": [
    "- 尽量不要使用 for...else..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3934436",
   "metadata": {},
   "source": [
    "## 6.2 案例故事\n",
    "- 计算某个文件中数字字符（0~9）的数量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d10c2d07",
   "metadata": {},
   "outputs": [],
   "source": [
    "def count_digits(fname):\n",
    "    \"\"\"计算文件中包含多少个数字字符\"\"\"\n",
    "    count = 0\n",
    "    with open(fname) as file:\n",
    "        for line in file:\n",
    "            for s in line:\n",
    "                if s.isdigit():\n",
    "                    count+=1\n",
    "    return count"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79a6d7aa",
   "metadata": {},
   "source": [
    "1. 读取文件的标准方法\n",
    "- 假如内容只有一行，会白白消耗大量时间和内存"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "262586a7",
   "metadata": {},
   "source": [
    "2. 使用while循环加read方法分块读取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d0a8155",
   "metadata": {},
   "outputs": [],
   "source": [
    "def count_digits_v2(fname):\n",
    "    \"\"\"计算文件里包含多少个数字字符，每次读取8kb\"\"\"\n",
    "    count = 0\n",
    "    block_size = 1024*8\n",
    "    with open(fname) as file:\n",
    "        while True:\n",
    "            chunk = file.read(block_size)\n",
    "            #当文件没有更多内容时，read调用将会返回空字符串''\n",
    "            if not chunk:\n",
    "                break\n",
    "            for s in chunk:\n",
    "                if s.isdigit():\n",
    "                    count+=1\n",
    "    return count"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7176e95a",
   "metadata": {},
   "source": [
    "3. iter()的另一个用法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7f6cdad",
   "metadata": {},
   "outputs": [],
   "source": [
    "from functools import partial\n",
    "\n",
    "def count_digits_v3(fname):\n",
    "    count = 0\n",
    "    block_size = 1024*8\n",
    "    with open(fname) as fp:\n",
    "        #使用functools.partial构造一个新的无需参数的函数\n",
    "        _read = partial(fp.read, block_size)\n",
    "\n",
    "        #利用iter()构造不断调用_read的迭代器\n",
    "        for chunk in iter(_read, ''):\n",
    "            for s in chunk:\n",
    "                if s.isdigit():\n",
    "                    count+=1\n",
    "    return count "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4397fc6",
   "metadata": {},
   "source": [
    "4. 按职责拆解循环体代码"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc994dae",
   "metadata": {},
   "source": [
    "## 6.3 编程建议\n",
    "### 6.3.1 中断嵌套循环的正确方式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "941bf134",
   "metadata": {},
   "outputs": [],
   "source": [
    "def find_first_word(fp, prefix):\n",
    "    \"\"\"找到文件里的第一个以指定前缀开头的单词并打印出来\n",
    "    :param fp:可读文件对象\n",
    "    :param prefix:需要寻找的单词前缀\n",
    "    \"\"\"\n",
    "\n",
    "    for line in fp:\n",
    "        for word in line.split():\n",
    "            if word.startswith(prefix):\n",
    "                return  word\n",
    "    return None\n",
    "\n",
    "def print_first_word(fp, prefix):\n",
    "    first_word = find_first_word(fp, prefix)\n",
    "    if first_word:\n",
    "        print(f'Found the first word startwish \"{prefix}\":\"{first_word}\"')\n",
    "    else:\n",
    "        print(f'Word starts with \"{prefix}\" was not found')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9e725e6",
   "metadata": {},
   "source": [
    "### 6.3.2 巧用next()函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9179d90f",
   "metadata": {},
   "outputs": [],
   "source": [
    "next(iter(d.keys()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4671fe48",
   "metadata": {},
   "source": [
    "### 6.3.3 当心已被耗尽的迭代器\n",
    "- 生成器可被“一次性耗尽”"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
